﻿using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Greycastle.RUBuildin
{
    public class ForegroundWindowWatcher : IDisposable
    {
        private readonly WinEventDelegate _eventHandler;
        private IntPtr _currentForegroundWindow;
        private readonly IntPtr _eventHookHandler;
        private bool _disposed;

        #region Windows event hook methods

        [DllImport("user32.dll")]
        static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        [DllImport("user32.dll")]
        static extern bool UnhookWinEvent(IntPtr hWinEventHook);

        delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        const uint WinEventOutOfContext = 0;
        const uint EventSystemForeground = 3;

        #endregion

        public ForegroundWindowWatcher()
        {
            _eventHandler = OnForegroundWindowChanged;
            _eventHookHandler = SetWinEventHook(EventSystemForeground, EventSystemForeground, IntPtr.Zero, _eventHandler, 0, 0, WinEventOutOfContext);
            _currentForegroundWindow = GetForegroundWindow();
        }

        private void OnForegroundWindowChanged(IntPtr hwineventhook, uint eventtype, IntPtr hwnd, int idobject, int idchild, uint dweventthread, uint dwmseventtime)
        {
            if (eventtype != EventSystemForeground)
                return;

            var foregroundWindow = GetForegroundWindow();
            if (_currentForegroundWindow == foregroundWindow)
                return;

            if(ForegroundWindowChanged != null)
                ForegroundWindowChanged(this, new ForegroundWindowChangedEventArgs(_currentForegroundWindow, foregroundWindow));

            _currentForegroundWindow = foregroundWindow;
        }

        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        public static bool IsForegroundWindow(IntPtr window)
        {
            return window == GetForegroundWindow();
        }

        public event ForegroundWindowChangedEventHandler ForegroundWindowChanged;
        
        public void Dispose()
        {
            if (_disposed)
                return;

            _disposed = true;
            UnhookWinEvent(_eventHookHandler);
        }

        [DllImport("user32.dll")]
        private static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);

        public static bool IsChildWindowOfOf(IntPtr childWindowHandle, IntPtr parentWindowHandle)
        {
            return IsChild(parentWindowHandle, childWindowHandle);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetWindowTextLength(IntPtr hWnd);

        public static string GetTitle(IntPtr windowHandle)
        {
            int length = GetWindowTextLength(windowHandle);
            var sb = new StringBuilder(length + 1);
            GetWindowText(windowHandle, sb, sb.Capacity);
            return sb.ToString();
        }
    }
}
